| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- "use client";
- import { useEffect, useState } from "react";
- import { useTranslations } from "next-intl";
- import { Link } from "@/i18n/navigation";
- import { useAuth } from "@/providers/auth-provider";
- import { FileText, ArrowLeft, Loader2 } from "lucide-react";
- import {
- cancelOrder,
- fetchOrderList,
- getOrderStatusLabel,
- type OrderRecord,
- } from "@/lib/order-api";
- import { cn } from "@/lib/utils";
- import { AccountLoginRequired } from "@/components/auth/account-login-required";
- const PAGE_SIZE = 10;
- export default function AccountOrdersPage() {
- const t = useTranslations("account");
- const { user, isReady } = useAuth();
- const [orders, setOrders] = useState<OrderRecord[]>([]);
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState<string | null>(null);
- const [page, setPage] = useState(1);
- const [total, setTotal] = useState(0);
- const [cancelTarget, setCancelTarget] = useState<OrderRecord | null>(null);
- const [cancelLoading, setCancelLoading] = useState(false);
- useEffect(() => {
- if (!user) return;
- let cancelled = false;
- async function loadOrders() {
- setLoading(true);
- setError(null);
- try {
- const res = await fetchOrderList({ current: page, row: PAGE_SIZE });
- if (cancelled) return;
- setOrders(res.list);
- setTotal(res.page.total);
- } catch (e) {
- if (cancelled) return;
- const err = e as Error;
- setError(err.message || "订单加载失败,请稍后重试。");
- setOrders([]);
- } finally {
- if (!cancelled) setLoading(false);
- }
- }
- void loadOrders();
- return () => { cancelled = true; };
- }, [user, page]);
- if (!isReady) return <div className="min-h-screen bg-[#050b14] flex items-center justify-center text-slate-500"><Loader2 className="animate-spin" /></div>;
- if (!user) {
- return <AccountLoginRequired />;
- }
- const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));
- async function handleConfirmCancel() {
- if (!cancelTarget) return;
- try {
- setCancelLoading(true);
- await cancelOrder(cancelTarget.id);
- const res = await fetchOrderList({ current: page, row: PAGE_SIZE });
- setOrders(res.list);
- setTotal(res.page.total);
- setCancelTarget(null);
- } catch (e) {
- setError((e as Error).message || "取消订单失败,请稍后重试。");
- } finally {
- setCancelLoading(false);
- }
- }
- function getStatusStyle(status: number | string) {
- const s = String(status);
- if (s === "2" || s === "3") return "text-emerald-400 border-emerald-400/30 bg-emerald-400/10";
- if (s === "4" || s === "5") return "text-rose-400 border-rose-400/30 bg-rose-400/10";
- return "text-amber-400 border-amber-400/30 bg-amber-400/10";
- }
- return (
- <div className="min-h-screen bg-[#050b14] pb-24 text-slate-300 font-sans relative">
- <div className="pointer-events-none fixed inset-0 z-0">
- <div className="absolute left-1/4 top-0 h-[500px] w-[500px] rounded-full bg-blue-900/10 blur-[120px]" />
- <div className="absolute right-1/4 bottom-0 h-[500px] w-[500px] rounded-full bg-[#b89458]/5 blur-[120px]" />
- </div>
- <div className="site-container relative z-10 pt-16">
- <div className="flex items-center justify-between mb-8">
- <div>
- <h1 className="font-serif text-3xl font-bold text-white">全部订单</h1>
- <p className="mt-2 text-sm text-slate-400">管理您的所有购买记录与账单明细</p>
- </div>
- <Link href="/account" className="flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-5 py-2.5 text-sm font-semibold text-slate-300 transition hover:bg-white/10 hover:text-white backdrop-blur-md">
- <ArrowLeft size={16} /> 返回控制中心
- </Link>
- </div>
- <section className="rounded-[2.5rem] border border-white/10 bg-white/5 p-6 md:p-10 backdrop-blur-2xl shadow-2xl">
- {loading ? <div className="py-20 flex justify-center text-slate-500"><Loader2 className="animate-spin h-8 w-8" /></div> : null}
- {error ? <div className="mb-6 rounded-2xl border border-rose-500/20 bg-rose-500/10 p-4 text-sm text-rose-400">{error}</div> : null}
- {!loading && !error && orders.length === 0 ? (
- <div className="py-20 flex flex-col items-center justify-center rounded-[2rem] border border-dashed border-white/10 bg-white/5 text-slate-500">
- <FileText size={48} className="mb-4 opacity-50" />
- <p className="text-sm">{t("noOrders")}</p>
- </div>
- ) : null}
- {!loading && !error && orders.length > 0 ? (
- <div className="space-y-4">
- {orders.map((o) => (
- <div key={o.serial} className="group rounded-[1.5rem] border border-white/5 bg-white/5 p-6 transition-all hover:bg-white/10">
- <div className="flex flex-col md:flex-row md:items-center justify-between gap-6">
- <div className="flex items-start gap-5">
- <div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl bg-white/5 text-[#f3deae]">
- <FileText size={22} />
- </div>
- <div>
- <p className="text-base font-bold text-white group-hover:text-[#f3deae] transition-colors">{o.details}</p>
- <div className="mt-2 flex flex-wrap items-center gap-x-4 gap-y-2 text-xs font-medium text-slate-500">
- <span>NO: {o.serial}</span>
- <span className="hidden sm:inline">•</span>
- <span>创建: {o.addTime || "-"}</span>
- {o.payTime && <><span className="hidden sm:inline">•</span><span>支付: {o.payTime}</span></>}
- </div>
- </div>
- </div>
- <div className="flex items-center justify-between md:justify-end gap-6 md:border-l md:border-white/5 md:pl-6">
- <div className="text-right">
- <p className="text-xl font-bold text-white tracking-tight">${o.amount}</p>
- <span className={cn("inline-block mt-1.5 rounded-full border px-3 py-1 text-[10px] font-bold uppercase tracking-widest", getStatusStyle(o.status))}>
- {getOrderStatusLabel(o.status)}
- </span>
- </div>
- {o.status === 1 && (
- <button onClick={() => setCancelTarget(o)} className="rounded-xl border border-rose-500/30 bg-rose-500/10 px-4 py-2 text-xs font-bold text-rose-400 transition hover:bg-rose-500/20">取消</button>
- )}
- </div>
- </div>
- </div>
- ))}
- </div>
- ) : null}
- {/* 分页 */}
- {!loading && !error && totalPages > 1 && (
- <div className="mt-10 flex items-center justify-center gap-4 border-t border-white/5 pt-8">
- <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={page <= 1} className="rounded-xl border border-white/10 bg-white/5 px-4 py-2 text-sm font-semibold transition hover:bg-white/10 disabled:opacity-30">上一页</button>
- <span className="text-sm font-medium text-slate-500">第 <span className="text-white">{page}</span> / {totalPages} 页</span>
- <button onClick={() => setPage(p => Math.min(totalPages, p + 1))} disabled={page >= totalPages} className="rounded-xl border border-white/10 bg-white/5 px-4 py-2 text-sm font-semibold transition hover:bg-white/10 disabled:opacity-30">下一页</button>
- </div>
- )}
- </section>
- </div>
- {/* 黑金风格确认弹窗 */}
- {cancelTarget && (
- <div className="fixed inset-0 z-50 flex items-center justify-center bg-[#050b14]/80 p-4 backdrop-blur-md">
- <div className="w-full max-w-sm overflow-hidden rounded-[2.5rem] border border-white/10 bg-[#0a1120] shadow-2xl">
- <div className="p-10 text-center">
- <div className="mx-auto mb-6 flex h-20 w-20 items-center justify-center rounded-full bg-rose-500/10 text-rose-400">
- <FileText size={32} />
- </div>
- <h3 className="text-xl font-bold text-white">确认撤销订单?</h3>
- <p className="mt-3 text-sm leading-relaxed text-slate-400 px-4">订单号: <span className="text-slate-200">{cancelTarget.serial}</span><br/>撤销后不可恢复。</p>
- </div>
- <div className="flex border-t border-white/5">
- <button onClick={() => setCancelTarget(null)} className="flex-1 py-5 text-sm font-bold text-slate-400 hover:bg-white/5">暂不处理</button>
- <div className="w-px bg-white/5" />
- <button onClick={handleConfirmCancel} disabled={cancelLoading} className="flex-1 py-5 text-sm font-bold text-rose-400 hover:bg-rose-500/10 disabled:opacity-50">
- {cancelLoading ? "执行中..." : "确认撤销"}
- </button>
- </div>
- </div>
- </div>
- )}
- </div>
- );
- }
|